use crate::{
    global::solve_quadratic,
    object::{DrawableObj, Object},
    vector::Vector3f,
};

pub struct Sphere {
    pub obj_attr: Object,
    pub center: Vector3f,
    pub radius: f32,
    pub radius2: f32,
}

impl Sphere {
    pub fn new(center: Vector3f, radius: f32) -> Self {
        Self {
            center,
            radius,
            radius2: radius * radius,
            obj_attr: Object {
                ..Default::default()
            },
        }
    }
}

impl DrawableObj for Sphere {
    fn intersect(
        &self,
        orig: Vector3f,
        dir: Vector3f,
        tnear: &mut f32,
        _index: &mut u32,
        _uv: &mut crate::vector::Vector2f,
    ) -> bool {
        let l = orig - self.center;
        let a = dir.dot_product(&dir);
        let b = 2.0 * dir.dot_product(&l);
        let c = l.dot_product(&l) - self.radius2;
        let mut t0 = 0.;
        let mut t1 = 0.;
        if !solve_quadratic(a, b, c, &mut t0, &mut t1) {
            false
        } else {
            if t0 < 0. {
                t0 = t1;
            }

            if t0 < 0. {
                false
            } else {
                *tnear = t0;
                true
            }
        }
    }

    fn eval_diffuse_color(&self, _st: crate::vector::Vector2f) -> Vector3f {
        self.obj_attr.diffuse_color
    }

    fn get_surface_properties(
        &self,
        hit_point: Vector3f,
        _dir: Vector3f,
        _index: u32,
        _uv: crate::vector::Vector2f,
        n: &mut Vector3f,
        _st: &mut crate::vector::Vector2f,
    ) {
        *n = (hit_point - self.center).normalize();
    }

    fn get_material_type(&self) -> crate::global::MaterialType {
        self.obj_attr.material_type
    }

    fn get_ior(&self) -> f32 {
        self.obj_attr.ior
    }

    fn get_specular_exponent(&self) -> f32 {
        self.obj_attr.specular_exponent
    }

    fn get_kd(&self) -> f32 {
        self.obj_attr.kd
    }
    fn get_ks(&self) -> f32 {
        self.obj_attr.ks
    }
}
